﻿using System;
using System.Collections.Generic;
using System.Windows;

namespace Red.Launcher.ViewModels.Workspaces.Database
{
    public abstract class UserGroup : Record
    {
        #region Implemented Properties

        public override string Icon { get { return "appbar_group"; } }
        public override IEnumerable<object> Values { get { return null; } }

        #endregion Implemented Properties
    }

    public sealed class VirtualUserGroup : UserGroup
    {
        #region Public Properties

        public static Metadata METADATA { get; private set; }

        #endregion Public Properties

        #region Implemented Properties

        public override Metadata Metadata { get { return METADATA; } }

        #endregion Implemented Properties

        #region C-Tor

        static VirtualUserGroup()
        {
            METADATA = new Metadata()
            {
                Table = "Group",
                Keys = new List<string>() { "name" },
            };

            METADATA.Children.Add( typeof( User ) );
        }

        public VirtualUserGroup()
        {
            IsVirtual = true;
        }

        #endregion C-Tor
    }

    public class AssignedUserGroup : UserGroup
    {
        #region Private Data

        private double? _stabilityWeight;
        private GroupStabilityWeight _stabilityWeightObject;

        #endregion Private Data

        #region Public Properties

        public static Metadata METADATA { get; private set; }

        public double? StabilityWeight
        {
            get { return _stabilityWeight; }

            set
            {
                if ( _stabilityWeightObject?.Weight != value )
                {
                    if( _stabilityWeightObject == null )
                    {
                        _stabilityWeightObject = new GroupStabilityWeight();
                    }

                    _stabilityWeight = value;
                    _stabilityWeightObject.Weight = value ?? 0;

                    OnPropertyChanged();
                }
            }
        }

        public GroupStabilityWeight GroupStabilityWeight { get { return _stabilityWeightObject; } private set { _stabilityWeightObject = value; } }

        /// <summary>
        /// Indicates if this Group has a stability weight entry in the database
        /// </summary>
        public bool HasStabilityWeight => !_stabilityWeightObject?.IsNewlyCreated ?? false;

        #endregion Public Properties

        #region Implemented Properties

        public override Metadata Metadata { get { return METADATA; } }
        public override IEnumerable<object> Values { get { return new List<object>() { Name }; } }

        #endregion Implemented Properties

        #region Drag and Drop

        internal override bool AcceptsDraggedData( IDataObject data )
        {
            return data.GetDataPresent( typeof( AssignedUser ) ) || data.GetDataPresent( typeof( User ) );
        }

        internal override void PerformDrop( IDataObject data )
        {
            DraggedRecord access = data.GetData( typeof( AssignedUser ) ) as DraggedRecord;

            if ( access != null )
            {
                Record child = access.Record;
                AddChild( ref child );

                //TODO: If this is a move, un-parent from original parent
            }
            else
            {
                DraggedRecord draggedUser = data.GetData( typeof( User ) ) as DraggedRecord;
                User user = draggedUser.Record as User;

                if ( user != null )
                {
                    AssignedUser existing = FindChild( user.Name ) as AssignedUser;

                    if ( existing == null )
                    {
                        Record record = new AssignedUser()
                        {
                            UserId = user.Id,
                            Name = user.Name,
                        };
                        base.AddChild( ref record );
                    }
                }
            }
        }

        #endregion Drag and Drop

        #region C-Tor

        static AssignedUserGroup()
        {
            METADATA = new Metadata()
            {
                Table = "Group",
                Keys = new List<string>() { "name" },
                CanCopy = true,
                CreateDefault = () => { return new AssignedUserGroup(); },
                CreateFromDatabaseRow = ( Dictionary<string, object> data ) =>
                {
                    object stabilityWeight = data[ "weight" ];

                    GroupStabilityWeight stabilityWeightObject = null;

                    if( !stabilityWeight.Equals( DBNull.Value ) )
                    {
                        stabilityWeightObject = new GroupStabilityWeight()
                        {
                            Id = (int)data[ "weight_id" ],
                            Weight = (double)stabilityWeight
                        };
                    }

                    return new AssignedUserGroup()
                    {
                        Id = (int)data[ "id" ],
                        Name = (string)data[ "name" ],
                        GroupStabilityWeight = stabilityWeightObject
                    };
                },
                GenerateSelectSql = GenerateSelectSql
            };

            METADATA.Children.Add( typeof( AssignedUser ) );
            METADATA.Joins.Add( typeof( GroupStabilityWeight ) );
            METADATA.Assignable.Add( typeof( User ) );
        }

        public AssignedUserGroup()
        {
        }

        #endregion C-Tor

        #region Sql

        private static Sql.Select GenerateSelectSql( Record parent )
        {
            Sql.Select.Table source = Sql.Select.CreateTable( AssignedUserGroup.METADATA.Table, "g" );

            Sql.Select query = new Sql.Select();
            query.Sources.Add( source );
            query.AddColumn( "*", source );

            if ( !parent.IsVirtual )
            {
                query.Conditions.AddLiteral( parent.Metadata.ForeignKey, parent.Id, source );
            }

            Sql.Select.Table rightSource = Sql.Select.CreateTable( GroupStabilityWeight.METADATA.Table );
            Sql.Select.Join join = Sql.Select.CreateJoin( rightSource, Sql.Select.JoinType.Left, "w" );
            join.Conditions.AddVar( "id", AssignedUserGroup.METADATA.ForeignKey, source, rightSource );
            query.Sources.Add( join );

            query.AddColumn( "id", join, "weight_id" );
            query.AddColumn( "weight", join );

            return query;
        }

        protected override void GenerateInsertSql( ref Sql.Transaction transaction, Record parent )
        {
            base.GenerateInsertSql( ref transaction, parent );

            if ( _stabilityWeight != null && !HasStabilityWeight )
            {
                Sql.Insert query = new Sql.Insert( GroupStabilityWeight );
                query.Parents.Add( this );

                transaction.Add( query );
            }
        }
        
        protected override void GenerateUpdateSql( ref Sql.Transaction transaction, Record parent )
        {
            base.GenerateUpdateSql( ref transaction, parent );

            if( HasStabilityWeight )
            {
                if ( _stabilityWeight == null )
                {
                    Sql.Delete query = new Sql.Delete( GroupStabilityWeight );

                    transaction.Add( query );
                }
                else
                {
                    Sql.Update query = new Sql.Update( GroupStabilityWeight );

                    transaction.Add( query );
                }
            }
            else
            {
                if( _stabilityWeight != null )
                {
                    Sql.Insert query = new Sql.Insert( GroupStabilityWeight );
                    query.Parents.Add( this );

                    transaction.Add( query );
                }
            }
        }

        protected override void GenerateDeleteSql( ref Sql.Transaction transaction, Record parent )
        {
            if ( HasStabilityWeight )
            {
                Sql.Delete query = new Sql.Delete( GroupStabilityWeight );

                transaction.Add( query );
            }

            base.GenerateDeleteSql( ref transaction, parent );
        }

        #endregion Sql
    }
}
